// .png texture loader by Semi Essessi
// only supports loading 3/4 channel pngs to a rgba/rgb0 unsigned int bitmap
// can be easily updated to support further types or perhaps ALL using the libpng functions

// Main header
#include "Main.h"

// My header
#include "SimplePNG.h"

//external
#include <stdio.h>

SimplePNG::SimplePNG()
{
	m_puImageData = 0;
	m_uWidth = 0;
	m_uHeight = 0;
}


SimplePNG::~SimplePNG()
{
	if(m_puImageData)
	{
		delete []m_puImageData;
	}
}


bool SimplePNG::LoadFromFile( const char* szFilename )
{
	char header[8];	// 8 character png header to be loaded and checked first
	
	FILE* f = 0;
	
	f = fopen(szFilename ,"rb");
	
	// if no file then return false
	if(!f) 
	{
		FridgeLogf("Texture Manager: Failed to open png file %s\n", szFilename);
		return false;
	}
	
	fread(header,1,8,f);
	// use libpngs header check function
	if(png_sig_cmp((png_bytep)header,0,8)) return false;

	// png struct pointer
	png_structp	pngptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

	if(!pngptr)
	{
		FridgeLogf("Texture Manager: Couldn't get png struct pointer %s\n", szFilename);
		fclose(f);
		return false;
	}

	// png info header
	png_infop infptr = png_create_info_struct(pngptr);

	if(!infptr)
	{
		FridgeLogf("Texture Manager: Couldn't get png info pointer %s\n", szFilename);
		png_destroy_read_struct(&pngptr, &infptr,(png_infopp)NULL);
		fclose(f);
		return false;
	}

	// read the image using libpng functions

	png_init_io(pngptr, f);
	png_set_sig_bytes(pngptr, 8);
	png_read_png(pngptr, infptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING, NULL);
	png_bytep* rp = png_get_rows( pngptr, infptr ); 
	int channels = png_get_channels(pngptr, infptr);
	m_uWidth = png_get_image_width(pngptr, infptr);
	m_uHeight = png_get_image_height(pngptr, infptr);

	if(!rp) 
	{
		FridgeLogf("Texture Manager: Couldn't read png image %s\n", szFilename);
		fclose(f);
		return false;
	}
	
	png_set_interlace_handling(pngptr);
	png_read_update_info(pngptr, infptr);

	fclose(f);
	
	// convert into a nice rgba m_puImageData array:

	m_puImageData = new u_int[ m_uWidth * m_uHeight ];

	if(!m_puImageData)
	{
		FridgeLogf("Texture Manager: Couldn't convert png image %s\n", szFilename);
		png_destroy_read_struct(&pngptr, &infptr,(png_infopp)NULL);
		fclose(f);
		return false;
	}

	// transform to rgba image data from 4 channels
	if(channels==4)
	{
		for(u_int y=0;y<m_uHeight;y++)
		{
			png_byte* row = rp[y];
			for(u_int x=0;x<m_uWidth;x++)
			{
				png_byte* p = &(row[x*4]);
				m_puImageData[y*m_uWidth+x]=p[0];			// red
				m_puImageData[y*m_uWidth+x]|=p[1]<<8;		// green
				m_puImageData[y*m_uWidth+x]|=p[2]<<16;		// blue
				m_puImageData[y*m_uWidth+x]|=p[3]<<24;		// alpha
			}
		}
	} else if(channels==3) // transform to rgba image data from 3 channels
	{
        for(u_int y=0;y<m_uHeight;y++)
		{
			png_byte* row = rp[y];
			for(u_int x=0;x<m_uWidth;x++)
			{
				png_byte* p = &(row[x*3]);
				m_puImageData[y*m_uWidth+x]=p[0];			// red
				m_puImageData[y*m_uWidth+x]|=p[1]<<8;		// green
				m_puImageData[y*m_uWidth+x]|=p[2]<<16;		// blue
				//m_puImageData[y*height+x]|=p[3]<<24;		// alpha
			}
		}
	} else if(channels==1) // transform to rgba image data from greyscale
	{
		for(u_int y=0;y<m_uHeight;y++)
		{
			png_byte* row = rp[y];
			for(u_int x=0;x<m_uWidth;x++)
			{
				png_byte* p = &(row[x]);
				m_puImageData[y*m_uWidth+x]=p[0];			// red
				m_puImageData[y*m_uWidth+x]|=p[0]<<8;		// green
				m_puImageData[y*m_uWidth+x]|=p[0]<<16;		// blue
				m_puImageData[y*m_uWidth+x]|=p[0]<<24;		// alpha
			}
		}
	} else
	{
		FridgeLogf("Texture Manager: Couldn't convert image colors %s\n", szFilename);
		delete []rp;	
		return false;
	}

	for(u_int j=0;j<m_uHeight;j++) 
	{
		delete []rp[j];
	}

	return true;
}

int SimplePNG::GetHeight() const
{
    return m_uHeight;
}

int SimplePNG::GetWidth() const
{
    return m_uWidth;
}

const void*	SimplePNG::GetImageData	( ) const
{
	return m_puImageData;
}

//eof
